import {NotFoundError, t} from 'elysia'
import {ElysiaApp} from '../index'
import crypto from 'crypto'
import {ForbiddenError, UnauthorizedError, encrypt, isAdmin, isSelf, removeToken} from '../utils/auth'
import {defaultPwd} from '../utils/const'
import {sysUser, sysUserRole} from '../schema/schema'
import {toSql} from '../utils/dw'
import {like, ne, and, count, eq, or} from 'drizzle-orm'
import {isUndefined} from 'lodash'

const addSchema = t.Object({
  name: t.String({maxLength: 20}),
  username: t.String({maxLength: 20}),
  email: t.Optional(t.String({format: 'email'})),
  phone: t.Optional(
    t.String({
      pattern:
        '((\\d{11})|^((\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1})|(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))$)',
    }),
  ),
  nickName: t.Optional(t.String()),
  remark: t.Optional(t.String()),
  avatar: t.Optional(t.String()),
})

const updSchema = t.Intersect([addSchema, t.Object({id: t.Number()})])

export default (app: ElysiaApp) =>
  app
    // 获取用户分页列表
    .get(
      '/page/list',
      async ({db, query}) => {
        const {page, pageSize = 10, name, phone} = query
        const {id, name: _name, username, avatar, email, phone: _phone, nickName, remark, updateTime} = sysUser
        const _res = await db.transaction(async tx => [
          await db
            .select({id, name: _name, username, avatar, email, phone: _phone, nickName, remark, updateTime})
            .from(sysUser)
            .where(
              and(
                toSql(name, e => like(_name, `%${e}%`)),
                toSql(phone, e => like(_phone, `%${e}%`)),
                ne(username, 'admin'),
              ),
            )
            .limit(pageSize)
            .offset((page - 1) * pageSize),
          await db
            .select({total: count()})
            .from(sysUser)
            .where(
              and(
                toSql(name, e => like(_name, `%${e}%`)),
                toSql(phone, e => like(_phone, `%${e}%`)),
                ne(username, 'admin'),
              ),
            ),
        ])
        return {list: _res[0], ..._res[1][0]}
      },
      {
        query: t.Object({
          page: t.Numeric(),
          pageSize: t.Optional(t.Numeric()),
          name: t.Optional(t.String()),
          phone: t.Optional(t.String()),
        }),
      },
    )
    // 获取用户下拉列表
    .get(
      '/list',
      async ({query, db}) => {
        const {name, phone} = query
        const {id, name: _name, username, avatar, email, phone: _phone, nickName} = sysUser
        return await db
          .select({id, name: _name, username, avatar, email, phone: _phone, nickName})
          .from(sysUser)
          .where(
            and(
              toSql(name, e => like(_name, `%${e}%`)),
              toSql(phone, e => like(_phone, `%${e}%`)),
              ne(username, 'admin'),
            ),
          )
      },
      {
        query: t.Object({
          name: t.Optional(t.String()),
          phone: t.Optional(t.String()),
        }),
      },
    )
    // 获取单个用户信息
    .get(
      '/info',
      async ({query: {id}, db}) => {
        return await db.query.sysUser.findFirst({
          columns: {id: true, name: true, username: true, avatar: true, email: true, phone: true, nickName: true, remark: true},
          where: eq(sysUser.id, id),
        })
      },
      {
        query: t.Object({
          id: t.Numeric(),
        }),
      },
    )
    // 新增用户
    .post(
      '/add',
      async ({body, db}) => {
        const {name, username, email, phone, nickName, remark} = body
        const _user = await db.query.sysUser.findFirst({
          where: or(
            eq(sysUser.username, username),
            toSql(phone, e => eq(sysUser.phone, e)),
          ),
        })
        if (!isUndefined(_user)) throw new ForbiddenError('此用户已存在，请勿重新添加')
        const pSalt = crypto.randomBytes(16).toString('hex')
        const iSalt = crypto.randomBytes(16).toString('hex')
        await db.insert(sysUser).values({
          name,
          username,
          password: encrypt(defaultPwd, pSalt, iSalt),
          avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
          email,
          phone,
          nickName,
          remark,
          pSalt,
          iSalt,
        })
        return '用户新增成功'
      },
      {body: addSchema},
    )
    // 修改用户
    .patch(
      '/upd',
      async ({body, db}) => {
        const {id, name, username, email, phone, nickName, remark, avatar} = body
        let _user = await db.query.sysUser.findFirst({
          where: or(
            eq(sysUser.username, username),
            toSql(phone, e => eq(sysUser.phone, e)),
          ),
        })
        if (_user && _user.id !== id) throw new ForbiddenError('此用户名或手机号已存在，请重试')
        _user = await db.query.sysUser.findFirst({where: eq(sysUser.id, id)})
        if (!_user) throw new NotFoundError('此用户不存在')
        if (_user.username === 'admin' && _user.username !== username) throw new ForbiddenError('管理员用户名不可更改')
        await db.update(sysUser).set({name, username, email, phone, nickName, remark, avatar}).where(eq(sysUser.id, id))
        return '用户修改成功'
      },
      {body: updSchema},
    )
    .patch(
      '/upd-pwd',
      async ({body, db, redis, store: {curUser}}) => {
        const {id, oldPwd, newPwd} = body
        if (oldPwd === newPwd) throw new ForbiddenError('原密码与新密码相同，请重试')
        const user = await db.query.sysUser.findFirst({where: eq(sysUser.id, id)})
        if (!user) throw new NotFoundError('此用户不存在')
        const {password, pSalt, iSalt} = user
        if (encrypt(oldPwd, pSalt, iSalt) === password) {
          // 原密码与用户密码一致
          const _pSalt = crypto.randomBytes(16).toString('hex')
          const _iSalt = crypto.randomBytes(16).toString('hex')
          await db
            .update(sysUser)
            .set({password: encrypt(newPwd, _pSalt, _iSalt), pSalt: _pSalt, iSalt: _iSalt})
            .where(eq(sysUser.id, id))
          // 删除token
          await removeToken(db, redis, id)
          if (isSelf(id, curUser)) {
            throw new UnauthorizedError('当前用户密码已更改，请重新登录')
          }
        } else {
          throw new ForbiddenError('用户原密码输入有误')
        }
        return '密码更改成功'
      },
      {
        body: t.Object({
          id: t.Number(),
          oldPwd: t.String(),
          newPwd: t.String(),
        }),
      },
    )
    // 删除用户
    .delete(
      '/del/:id',
      async ({params: {id}, db, store: {curUser}}) => {
        if (!(await isAdmin(db, curUser))) throw new ForbiddenError('普通用户不可删除，请联系管理员操作')
        if (isSelf(id, curUser)) throw new ForbiddenError('不可删除当前登录用户')
        await db.transaction(async tx => {
          await tx.delete(sysUserRole).where(eq(sysUserRole.userId, id))
          await tx.delete(sysUser).where(eq(sysUser.id, id))
        })
      },
      {
        params: t.Object({
          id: t.Numeric(),
        }),
      },
    )
